package org.jsequences.reflect;

import com.google.common.base.Predicate;
import com.google.common.reflect.TypeToken;


/**
 * An abstract class which calls a static method of a class with exactly one
 * parameter by reflection. Use this class to create Method-References in a
 * concise way:
 * 
 * <pre class="code">
 * <code class="java">
 * public class Persons {
 *   public static boolean isAdult(Person p){..}
 *   
 *   public static final Predicate&lt;Person&gt; IS_ADULT =
 *     new MethodPredicate&lt;Persons,Person&gt;("isAdult"){};
 * }
 * </code>
 * </pre>
 * <p>
 * <b>Usage notes:</b> Instantiate this class <b>ONLY</b> in static final constants! Since the
 * initialization phase is unsafe thanks to reflection, refactorings might break the code.
 * Therefore, ensure that the Function is a constant and stays <b>as close to the method declaration
 * as possible</b>! After the static initialization is successful, the function is completely
 * typesafe and nearly as fast as implementing the anonymous function by hand. <i>As long as you
 * adhere to these guidelines, no refactoring should omit your reflective method and if it is, the
 * error is fail-fast and trivial to detect!</i>
 * <p>
 * <b>Implementation notes:</b> This class is abstract but without a single member to force all
 * users to instantiate it with an empty block {} as shown above. This is a technique called
 * "generic type capture", see {@link TypeToken}.
 * 
 * @param <C> the type of the owner class
 * @param <I> the input type
 * @author Eric Giese
 */
public abstract class StaticPredicate<C, I> extends ReflectionMethod<C, Boolean>
        implements Predicate<I> {

    /**
     * Instantiates this abstract type.
     * 
     * @param name the exact name of the method to call.
     */
    protected StaticPredicate(String name) {
        initStaticMethod(name, new TypeToken<I>(getClass()) {}.getRawType());
    }

    /**
     * Calls the MethodReference on the instance and returns the result.
     * 
     * @param input the object instance
     * @return the method result
     */
    public final boolean apply(I input) {
        return invoke(null, input);
    }

    /**
     * StaticPredicates are equal only to other StaticPredicates.
     */
    @Override
    protected final boolean canEqual(Object other) {
        return other instanceof StaticPredicate;
    }
}
